------------------------------------------------------------------------------------------------------------------------
-- Name: remoter
-- Func: receive events from device
-- Author       : Wei Hua
-- contributor	: mslack
-- Creation Date: 2011-7
-- Last Update  : 2012-06
--
------------------------------------------------------------------------------------------------------------------------
-- FEATURES current supported:
-- accelerometer
-- tuner
-- native inputbox
-- multitouch
-- screen caputure (for accurate touch on device)
-- heading (compass)
-- location (GPS)

------------------------------------------------------------------------------------------------------------------------
-- File in package:
-- remoter.lua	wrapped functions (All funtions you need are in this file)
-- whnet.lua	net functions
-- base64.lua	base64 codec (for screen capture file transmission)
------------------------------------------------------------------------------------------------------------------------
--[[
Usage:
1. copy the files (remoter.lua, whnet.lua, base64.lua) to your source code directory
2. add:
   require("remoter")
   remoter.startService("udp", 1974)
   to your main.lua file
3. use the functions you needed to active the specific events

Function list:
1. startService(protocol, port)
   function:
   Start the remoter service, so the app on device can connect to the simulator and send events to it
   param:
   protocol			a string. If it is "udp", then use the udp protocol to transmit events and cmds, otherwise use tcp.
   port				listening port. default is 1974
2. endService()
   function:
   Terminate the remoter service. You normally do not need this function. You can just close the simulator to terminate everything.
   param:
   none
3. startTuner()
   function:
   Start to update tuner info
   param:
   none
4. endTuner()
   function:
   Stop updating tuner info
   param:
   none
5. getTunerFrequency()
   function:
   return tuner frequency. like Tuner:getTunerFrequency()
   param:
   none
6. getTunerVolume()
   function:
   return tuner volume. like Tuner:getTunerVolume()
   param:
   none
7. createTextField(param)
   function:
   Create and return a textfield object (in simulator, it is a lable to represent textfield. when touched a real textfield will be activated on device where coremoter is running)
   param:
   param is a table. its contents are as following:
	-- text			original text to display
	-- x,y			letftop pos
	-- w,h			width and height
	-- font			font of the text input field
	-- fontsize		font size of the text in the text input field
   A textfield object has following extended functions:
   setText
   NOTATION:
   You should use f:setText(text) (instead of "f.text=text") to set a input field's content

Functional Variables:
1. g_bShowTouchPoint
   set this to true, then you can see the touch point in simulator

SDK hacking:
   In simulator, Runtime.addEventListener, Runtime.removeEventListener, native.setKeyboardFocus and display.getCurrentStage().setFocus are replaced by my functions
   No code change should be made for accelerometer,heading and location events. (Only function listener is supported now)

Multitouch rule:
   To use multitouch in simulator, you must following one rule:
   When you addEventListener to an object, the second param of the function must be the object itself and the object must have a touch function.
   (Because it is the only way I can find whether an object can process touch events)
   If you add a global "touch" listener (by Runtime:addEvent), the listener will surely receive the multitouch events if no display object "eat" them :)

The tricks of using TCP:
   There is two advantage of using TCP: 
   (a)you can manually send special events by telnet.
   e.g.
   1. telnet 127.0.0.1 1974
   2. type
      cmd=accelerometer isShake=true
	  then hit return
   You will generate a shake event in simulator

   To find more cmd format, just uncomment "--print("****", msg)" and fint out by yourself in console output
   (b)TCP will never lose data but UDP may have a litte chance to lose data even in LAN


Following are some examples about how to modify Corona SDK Sample Code to accept events from coremoter.
You will find it is very easy.

1. DebugDraw
copy the files (remoter.lua, whnet.lua, base64.lua) to DebugDraw source code directory

in main.lua

add following code at the beginning of file

require("remoter")
remoter.startService("udp", 1974)

find
j:addEventListener( "touch", dragBody )

change it to following:
function j:touch(event)
dragBody(event)
end
j:addEventListener( "touch", j )

then, run it in simulator
open coremoter
connect to youreip:1974
turn multitouch on

2. ShapeTumbler
copy the files (remoter.lua, whnet.lua, base64.lua) to ShapeTumbler source code directory

in main.lua

add following code at the beginning of file

require("remoter")
remoter.startService("udp", 1974)

--]]
------------------------------------------------------------------------------------------------------------------------

module(..., package.seeall)

------------------------------------------------------------------------------------------------------------------------
-- network lib
------------------------------------------------------------------------------------------------------------------------
local whnet 	= require("whnet")

-- set this to true, if you want to see the track of touch points
g_bShowTouchPoint	= false

local	buildVersion	= system.getInfo("build")
local	platformName	= system.getInfo("platformName")

-- check if it is iphone4 (在自动适应屏幕尺寸时，textfield的位置有问题。至少在build 2011.701时还存在这个bug)
local	display_width	= display.contentWidth
-- 外面也会用到这个参数用来调整界面
g_isiphone4		= (display_width == 640)
------------------------------------------------------------------------------------------------------------------------
-- common functions
------------------------------------------------------------------------------------------------------------------------
function toboolean(_v)
	if( _v=="true" or _v=="1" ) then return true end
	return	false
end

function fromstrbytype(str, typename)
	if( typename=="number" ) then
		return	tonumber(str)
	end
	if( typename=="boolean" ) then
		return	toboolean(str)
	end
	return	str
end
function ParseArgs(s, arg)
	if( not arg ) then arg = {} end
	-- 所有用引号扩着的都作为value
	string.gsub(s, "(%w+)%s*=%s*([\"'])(.-)%2"
		, function (w, _, a)
			arg[w] = fromstrbytype(a, type(arg[w]))
		end)
	-- 没有引号的，则看等号后的第一个非空连续串
	string.gsub(s, "(%w+)%s*=%s*([^%s>]+)"
		, function (w, a)
			if not arg[w] then
				arg[w] = fromstrbytype(a, type(arg[w]))
			end
		end)
	return arg
end
function table_removevalue(_table, _val)
	for i=1,#_table,1 do
		if( _table[i]==_val ) then
			_table[i]	= nil
			table.remove(_table, i)
			return	true
		end
	end
	return	false
end

function addDefaultData(_data, _default)
	-- 浏览_default的每一项，看_data中是否存在
	-- 如果不存在，则把default中的数据添加到_data中
	local function _traverse(_dnode, _snode)
		if( not _snode ) then return false end
		for k,v in pairs(_snode) do
			-- 判断_dnode中是否有对应的项目
			local	dv	= _dnode[k]
			if( dv~=nil ) then 
				if( type(v)=="table" and type(dv)=="table") then
					-- 必须两个都是table才能继续
					if( #dv>1 ) then
						-- 单纯的数组，则就还是保持dv中的数据即可
					else
						-- 继续看里面
						_traverse(dv, v)
					end
				end
			else
				-- 如果data中不存在，则创建这个默认数据
				if( type(v)=="table" ) then
					-- 如果是table，则需要拷贝数据
					_dnode[k]	= copyData(v)
				else
					-- 如果不是table，就可以直接赋值了
					_dnode[k]	= v
				end
			end
		end
	end

	_traverse(_data, _default)
end

function getTableFromStr(_str, _trans)
	local	rst	= {}
	string.gsub(_str, "([^%s]+)"
		, function (w) if( _trans ) then w=_trans(w); end; table.insert(rst, w); end
		)
	return	rst
end

function extend_removeSelf(self, newfunc)
	local	old	= self.removeSelf
	if( not old ) then
		error("There is no removeSelf for this obj!")
		return	false
	end
	self.removeSelf	= function (self)
		-- 新的内容
		newfunc(self)
		-- 调用旧的
		old(self)
	end
	return	true
end

local systemType	= system.getInfo( "environment" )
if( systemType=="simulator" ) then
	isSimulator	= true
end

-- 存储在模拟器时的监听函数
local g_heading_listener	= {}
local g_location_listener	= {}
local g_accelerometer_listener	= {}
local g_accelerometer_interval	= 60
-- tuner相关参数
local g_Tuner					= nil
local g_TunerFrequency			= 0
local g_TunerVolume				= 0

local g_port	= 1974
local g_svr		= nil
-- 各种种处理函数
local	cmds		= {
	accelerometer	=
		function (event)
			-- 直接把这个传给accelerometer的监听函数即可
			if( event.isShake=="true" ) then
				event.isShake	= true
			else
				event.isShake	= false
			end
			event.xGravity	= tonumber(event.xGravity)
			event.yGravity	= tonumber(event.yGravity)
			event.zGravity	= tonumber(event.zGravity)
			event.xInstant	= tonumber(event.xInstant)
			event.yInstant	= tonumber(event.yInstant)
			event.zInstant	= tonumber(event.zInstant)
			for i=1,#g_accelerometer_listener,1 do
				g_accelerometer_listener[i](event)
			end
		end
	, heading		=
		function (event)
			for i=1,#g_heading_listener,1 do
				g_heading_listener[i](event)
			end
		end
	, location		=
		function (event)
			for i=1,#g_location_listener,1 do
				g_location_listener[i](event)
			end
		end
	, tuner			=
		function (event)
			g_TunerFrequency	= tonumber(event.Frequency)
			g_TunerVolume		= tonumber(event.Volume)
--print("****", system.getTimer(), g_TunerFrequency, g_TunerVolume)
		end
	-- 这个起名和corona中的event一致
	, userInput			=
		function (event)
			local	field	= g_curTextField
			if( g_curTextField ) then
				if( event.phase=="editing" ) then
					-- 如果编辑过程，则需要更新一下text内容
					g_curTextField:setText(event.text)
				elseif( event.phase=="submitted" ) then
					-- 结束时更新一下（中文的变化不会更新在editing消息中）
					field:setText(event.text)
					-- 真正的信息里面没有text这个内容
					event.text	= nil
					-- 如果是submitted则，需要取消焦点
					native.setKeyboardFocus(nil)
				elseif( event.phase=="ended" ) then
					if( g_prevTextField ) then
						field	= g_prevTextField
					end
					field:setText(event.text)
					-- 真正的信息里面没有text这个内容
					event.text	= nil
				end
				-- 模拟和textfield一样的event内容
				field._my_listener:userInput(event)
			end
		end
	-- 请求获取当前截屏
	, screenCap			=
		function (event)
			-- 截屏到tmp目录
			display.save( display.getCurrentStage(), "screen.jpg", system.TemporaryDirectory )
			-- 传递文件给请求者
			g_svr:sendFile("screen.jpg", system.TemporaryDirectory)
		end
	-- 显示触摸点
	, showTouchPoint	=
		function (event)
			if( event.value=='true' ) then
				g_bShowTouchPoint	= true
			else
				g_bShowTouchPoint	= false
			end
		end
	-- 多点触摸
	, touch				=
		function (event)
			event.cmd		= nil
			-- 把字串改为数字
			event.x			= tonumber(event.x)
			event.y			= tonumber(event.y)
			event.xStart	= tonumber(event.xStart)
			event.yStart	= tonumber(event.yStart)
			event.time		= tonumber(event.time)
			-- 找到当前focus的对象，如果没有就传给runtime
			local	obj	= g_focus_r[event.id]
			if( obj ) then
				-- 这个event对应这个对象
				event.target	= obj
				-- 都传给他便是了
				obj:dispatchEvent(event)
			elseif( g_allfocus ) then
				-- 所有都传给这个对象
				event.target	= g_allfocus
				g_allfocus:dispatchEvent(event)
			else
				local function _check(obj)
					-- 首先这个对象不能已经设置了focus
					if( g_focus[obj] ) then
						return	false
					end
					-- 如果obj没有touch方法也不行
					if( type(obj.touch)~="function" ) then
						return	false
					end
					-- 如果没有focus也需要target的，比如began时就需要
					event.target	= obj
					-- 如果对象自己的touch方法中返回true，则表示不需要再今后后续的查找了
					return	obj:touch(event)
				end
				-- 找在范围内的最上面的对象
				local	obj	= getObjByPos(event.x,event.y,_check)
				if( not obj ) then
					-- 如果没有就只能给runtime了
					Runtime:dispatchEvent(event)
				end
			end
			-- 看看是否需要画轨迹
			if( g_bShowTouchPoint ) then
				-- 创建一个自己消失的小球
				local	c	= display.newCircle(event.x,event.y,30)
				c:setFillColor(0,255,0,128)
				transition.to(c, {time=500,alpha=0.2,onComplete=function() c:removeSelf() end})
			end
		end
}

-- 把自己当前的屏幕尺寸发给客户端
function sendScreenInfo()
	g_svr:sendMsg("cmd=screenInfo w="..display.contentWidth.." h="..display.contentHeight.." orientation="..system.orientation)
end

------------------------------------------------------------------------------------------------------------------------
-- focus table
------------------------------------------------------------------------------------------------------------------------
-- obj -> id
g_focus		= {}
-- id -> obj
g_focus_r	= {}
g_allfocus	= nil
-- 清除所有传来的event焦点（这个在客户端断线时使用）
function clearAllFakeFocus()
	local	oldfunc	= display.getCurrentStage()._old_setFocus
	if( g_allfocus ) then
		print("**** clear fake global foucs:", tostring(obj))
		-- 取消全局focus
		oldfunc(display.getCurrentStage(), nil)
		-- 清空变量
		g_allfocus	= nil
	end
	for obj,id in pairs(g_focus) do
		if( type(id)=="string" ) then
			print("**** clear fake foucs:", tostring(obj), tostring(g_focus[obj]))
			-- 对于真有的focus要清除
			oldfunc(display.getCurrentStage(), obj, nil)
			-- 变量清空
			g_focus[obj]	= nil
			g_focus_r[id]	= nil
		end
	end
end

------------------------------------------------------------------------------------------------------------------------
-- 总的服务（如果不是simulator就不用开）
------------------------------------------------------------------------------------------------------------------------
local function _onexit_service(event)
	if( event.type=="applicationExit" ) then
		print("**** onexit_service ...")
		endService()
	end
end
local function _orientationListener(event)
	sendScreenInfo()
end
function startService(udp, port)
	if( isSimulator ) then
		if( not g_svr ) then
			if( port ) then
				g_port	= port
			end
			if( udp=="udp" ) then
				print("**** starting UDP server ...")
				g_svr	= whnet.createUDPServer()
			else
				print("**** starting TCP server ...")
				g_svr	= whnet.createTCPServer()
			end
			
			function g_svr:onAccept()
				print("**** client from "..g_svr:getpeername())
				-- 把自己当前的屏幕尺寸发过去
				sendScreenInfo()
				-- 保险起见（比如没有觉察到断线），清理一下门户
				-- 取消之前的所有非系统的focus
				clearAllFakeFocus()
				-- 把之前的textField取消
				unActivateTextField()
			end
			function g_svr:onClose()
				print("**** client disconnected")
				-- 取消之前的所有非系统的focus
				clearAllFakeFocus()
				-- 把之前的textField取消
				unActivateTextField()
			end

			--local count=0
			function g_svr:onMsg(msg)
			--count=count+1
			-- uncomment this to see the detail of string cmds
--print("****", msg)
				-- 解析xml
				local	msg	= ParseArgs(msg)
				local	f	= cmds[msg.cmd]
				if( f ) then
					f(msg)
				end
			end
		end
		-- 如果发现是模拟器就打开服务器
		local	rst,err	= g_svr:startServer(g_port,true)
		print("**** startServer at port:"..g_port,"rst:"..tostring(rst))
		if( rst ) then
			-- 监控一下退出消息
			Runtime:addEventListener("system", _onexit_service)
		end
		-- 在每次姿态变换时发送屏幕信息给客户端
		Runtime:addEventListener("orientation", _orientationListener)
		------------------------------------------------------------
		-- 各种函数替换
		------------------------------------------------------------
		-- 替换setFocus
		------------------------------------------------------------
		local oldfunc	= display.getCurrentStage().setFocus
		display.getCurrentStage().setFocus	= function(self, obj, id)
			-- 先判断obj是否已经在单独对应id的表中
			local	oldid	= g_focus[obj]
			if( oldid ) then
				if( id ) then
					-- 不应该给一个对象同时focus到两个eventid的
					return	false
				end
				-- 之前用来对应一个eventid（focus）
				-- 这次的应该就是取消了
				g_focus[obj]		= nil
				g_focus_r[oldid]	= nil
				if( type(oldid)=="string" ) then
					-- 假的就不用调用系统函数了
					return
				end
				return	oldfunc(self, obj, nil)
			end
			-- setFocus可能是根据参数个数来判断执行什么操作的，所以oldfunc(self,obj,nil)和oldfunc(self,obj)执行结果不同
			-- 让obj焦点到id上
			if( id ) then
				-- 记录一下对应关系
				g_focus[obj]	= id
				g_focus_r[id]	= obj
				if( type(id)=="string" ) then
					-- 说明这个是处理"假"的event时的操作，就不用调用系统函数了
					return
				end
				return	oldfunc(self, obj, id)
			end
			-- 让obj获得所有焦点（或取消）
			g_allfocus	= obj
			return	oldfunc(self, obj)
		end
		display.getCurrentStage()._old_setFocus	= oldfunc
		------------------------------------------------------------
		-- 替换Runtime:addEventListener / removeEventListener (主要是为了accelerometer)
		------------------------------------------------------------
		local oldfunc	= Runtime.addEventListener
		Runtime.addEventListener	= function(self, name, listener)
			if( name=="accelerometer" ) then
				table.insert(g_accelerometer_listener, listener)
				return
			elseif( name=="heading" ) then
				table.insert(g_heading_listener, listener)
				return
			elseif( name=="location" ) then
				table.insert(g_location_listener, listener)
				return
			end
			return	oldfunc(self, name, listener)
		end
		Runtime._old_addEventListener	= oldfunc
		
		local oldfunc	= Runtime.removeEventListener
		Runtime.removeEventListener	= function(self, name, listener)
		--print("**** removeEventListener", name, listener)
			if( name=="accelerometer" ) then
				table_removevalue(g_accelerometer_listener, listener)
				return
			elseif( name=="heading" ) then
				table_removevalue(g_heading_listener, listener)
				return
			elseif( name=="location" ) then
				table_removevalue(g_location_listener, listener)
				return
			end
			return	oldfunc(self, name, listener)
		end
		Runtime._old_removeEventListener	= oldfunc

		return	rst,err
	end
	return	true
end
function endService()
	if( isSimulator ) then
		Runtime:removeEventListener("system", _onexit_service)
		Runtime:removeEventListener("orientation", _orientationListener)
		-- turn off the server if it is on
		g_svr:endServer()
		------------------------------------------------------------
		-- 恢复原来的函数
		------------------------------------------------------------
		-- 判断旧函数是否存在（主要是怕endService被调用多次）
		if( display.getCurrentStage()._old_setFocus ) then
			display.getCurrentStage().setFocus		= display.getCurrentStage()._old_setFocus
			display.getCurrentStage()._old_setFocus	= nil
		end
		if( Runtime._old_addEventListener ) then
			Runtime.addEventListener	= Runtime._old_addEventListener
		end
		if( Runtime._old_removeEventListener ) then
			Runtime.removeEventListener	= Runtime._old_removeEventListener
		end
	end
end

------------------------------------------------------------------------------------------------------------------------
-- 加速度感应（2001-09-15 改为直接替换add/removeEventListener）
------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------
-- 音频控制
------------------------------------------------------------------------------------------------------------------------
if( isSimulator ) then
	function startTuner()
		-- 啥也不用做，因为是等网络传来信息
	end
	function endTuner()
		-- 啥也不用做，因为是等网络传来信息
	end
	function getTunerFrequency()
		return	g_TunerFrequency
	end
	function getTunerVolume()
		return	g_TunerVolume
	end
else
	function startTuner()
		g_Tuner	= media.newRecording()
		g_Tuner:startTuner()
		g_Tuner:startRecording()
	end
	function endTuner()
		if( g_Tuner ) then
			g_Tuner:stopRecording()
			g_Tuner:stopTuner() 
			g_Tuner	= nil
		end
	end
	function getTunerFrequency()
		return	g_Tuner:getTunerFrequency()
	end
	function getTunerVolume()
		return	g_Tuner:getTunerVolume()
	end
end

------------------------------------------------------------------------------------------------------------------------
-- lable
------------------------------------------------------------------------------------------------------------------------
-- create a lable with white background
-- param:
-- parent		parent object
-- text			text to display
-- x,y			letftop pos
-- w,h			width and height
-- font
-- fontsize
-- textcolor	color of text
-- bkcolor		color of backgroud
function createLable(param)
	local	myparam	= {
		x			= 0
		, y			= 0
		, w			= 100
		, h			= 22
		, font		= native.systemFont
		, fontsize	= 16
		, textcolor	= "0 0 0 255"
		, bkcolor	= "255 255 255 255"
	}
	addDefaultData(param, myparam)
	local	textcolor	= getTableFromStr(param.textcolor, tonumber)
	local	bkcolor		= getTableFromStr(param.bkcolor, tonumber)
	
	local	bg	= display.newGroup()
	if( param.parent ) then
		param.parent:insert(bg)
	end
	bg._back	= display.newRect(bg,0,0,param.w,param.h)
	bg._back:setFillColor(bkcolor[1], bkcolor[2], bkcolor[3], bkcolor[4])
	bg._text	= display.newText(bg, "", 0,0, font, param.fontsize)
	bg._text:setTextColor(textcolor[1], textcolor[2], textcolor[3], textcolor[4])
	function bg:setText(text)
		if( not text ) then
			text	= ""
		end
		self._text.text	= text
		self.text		= text
		self._text:setReferencePoint(display.TopLeftReferencePoint)
		self._text.x	= 1
		self._text.y	= 1
	end
	function bg:setTextColor(c)
		c	= getTableFromStr(c, tonumber)
		self._text:setTextColor(c[1], c[2], c[3], c[4])
	end
	function bg:setBKColor(c)
		c	= getTableFromStr(c, tonumber)
		self._back:setFillColor(c[1], c[2], c[3], c[4])
	end
	function bg:setBorder(c, w)
		if( not c ) then
			self._back.strokeWidth	= 0
		else
			c	= getTableFromStr(c, tonumber)
			self._back:setStrokeColor(c[1], c[2], c[3], c[4])
			if( not w ) then w=2 end
			self._back.strokeWidth	= w
		end
	end
	function bg:getWidth()
		return	self._back.contentWidth
	end
	function bg:getHeight()
		return	self._back.contentHeight
	end

	bg:setText(param.text)
	bg.x	= param.x
	bg.y	= param.y
	return	bg
end

-- current focused fake textfield
g_curTextField		= nil
g_prevTextField		= nil
-- activate a fake text field
function activateTextField(obj)
	if( g_curTextField==obj ) then
		-- the same field. no need to activate again.
		return
	end
	unActivateTextField()
	g_curTextField	= obj
	obj:setBorder("100 255 0 255")
	-- send cmd
	local	cmd		= "cmd=openInput text=\""..obj.text.."\" w="..g_curTextField:getWidth().." h="..g_curTextField:getHeight().." x="..g_curTextField.x.." y="..g_curTextField.y.." fontsize="..g_curTextField._my_fontsize
	g_svr:sendMsg(cmd)
end
function unActivateTextField()
	if( g_curTextField ) then
		g_prevTextField	= g_curTextField
		g_curTextField:setBorder()
		g_curTextField	= nil
		-- send cmd
		local	cmd		= "cmd=closeInput"
		g_svr:sendMsg(cmd)
	end
end
------------------------------------------------------------------------------------------------------------------------
-- textfield
------------------------------------------------------------------------------------------------------------------------
-- create a fake textfield
------------------------------------------------------------------------------------------------------------------------
-- param:
-- text			original text to display
-- x,y			letftop pos
-- w,h			width and height
-- font
-- fontsize
-- Notation:
-- You should use f:setText(text) to set a input field's content
local function _textfield_removeself(self)
	unActivateTextField()
end
local function _textfield_setText(self, text)
	self.text	= text
end
local function _dft_textfield_listener(event)
	--if( event.phase=="ended" or event.phase=="submitted" ) then
	if( event.phase=="submitted" ) then
		native.setKeyboardFocus(nil)
	end
end
function createTextField(param, listener)
	local	myparam	= {
		x			= 0
		, y			= 0
		, w			= 100
		, h			= 22
		, font		= native.systemFont
		, fontsize	= 16
		, scale		= 1
	}
	addDefaultData(param, myparam)

	local	t
	--if( isSimulator and (platformName=='Win' or buildVersion<'2011.690')) then
	if( isSimulator ) then
		-- just create a lable
		t	= createLable({x=param.x, y=param.y, w=param.w, h=param.h, text=param.text, font=param.font, fontsize=param.fontsize})
		if( type(listener)=="table" ) then
			t._my_listener	= listener
		elseif( type(listener)=="function" ) then
			t._my_listener	= {userInput=function(self, event) listener(event); end}
		else
			t._my_listener	= {userInput=function(self, event) _dft_textfield_listener(event)  end}
		end
		t._my_fontsize	= param.fontsize
		-- touch to set focus to it
		function t:touch(event)
			if( event.phase=="began" ) then
				-- activate the input textfield on device
				activateTextField(self)
			end
			return	true
		end
		t:addEventListener("touch", t)
		extend_removeSelf(t, _textfield_removeself)
	else
		if( not listener ) then
			listener	= _dft_textfield_listener
		end
		if( g_isiphone4 ) then
			t	= native.newTextField(param.x, param.y, param.w/2, param.h/2, listener)
			t.font		= native.newFont(param.font, math.floor(param.fontsize/2))
			-- 设置位置函数
			local function _setPos(self, x,y)
				self.x,self.y	= x/2, y/2
			end
			t.setPos	= _setPos
		else
			t	= native.newTextField(param.x, param.y, param.w, param.h, listener)
			t.font		= native.newFont(param.font, param.fontsize)
		end
		-- 设置成左上角对齐
		t:setReferencePoint(display.TopLeftReferencePoint)
		-- 这个是为了和simulator的保持一致
		t.setText	= _textfield_setText
		t:setText(param.text)
	end
	if( param.scale~=1 ) then
		t.xScale	= param.scale
		t.yScale	= param.scale
	end
	return	t
end
------------------------------------------------------------------------------------------------------------------------
-- replace native.setKeyboardFocus
------------------------------------------------------------------------------------------------------------------------
if( isSimulator ) then
	native.setKeyboardFocus	= 
	function (obj)
		if( obj ) then
			activateTextField(obj)
		else
			unActivateTextField()
		end
	end
end

-- 判断一个对象是否是group
function	isGroup(obj)
	return	type(obj.numChildren)=="number"
end

-- 判断一个点是否在一个对象内部（粗略判断，对象不能是group）
function	isPointInObj(obj, x, y)
	-- 首先对象必须可见或者hittestable
	if( not obj.isVisible and not obj.isHitTestable ) then
		return	false
	end
	-- 记录旧的参考点
	local	oldx,oldy	= obj.xReference,obj.yReference
	-- 先改成中心对齐
	obj:setReferencePoint(display.CenterReferencePoint)
	-- 判断是否在范围内
	local	rst	= math.abs(x-obj.x)<obj.width/2 and math.abs(y-obj.y)<obj.height/2
	-- 恢复旧的参考点
	obj.xReference,obj.yReference	= oldx,oldy
	-- 返回结果
	return		rst
end

function vect_rotate_by_xy(x,y, angle)
	angle	= angle*math.pi/180
	local	c,s	= math.cos(angle), math.sin(angle)
	return	x*c - y*s, x*s + y*c
end

-- 判断点在group的哪子个对象内部（x,y之前应该是相对于g的父对象的坐标）
-- 如果只想找到第一个，则让func直接返回true即可
-- 返回：
-- ok,checkgrp
-- ok		如果找到合适的对象，则返ok为true，否则为nil
-- checkgrp	如果找到对象但是所有对象都不给力，则checkgrp为真，检查一下group是否合适
local function	_getObjByPos(g,x,y,func)
	local	rst
	local	checkgrp	= nil
	-- 如果group本身就不可见则也不能继续
	if( not g.isVisible and not g.isHitTestable ) then
		return;
	end
	-- 先把x,y转换为相对于g的坐标（要考虑g本身的缩放）
	x,y	= (x-g.x)/g.xScale,(y-g.y)/g.yScale
	if( g.rotation~=0 ) then
		x,y	= vect_rotate_by_xy(x,y,-g.rotation)
	end
	--for i=1,g.numChildren,1 do
	-- fixed by mslack 2012-06-02 原来对象的检索顺序不对。反了。越后插入的对象越靠前。
	for i=g.numChildren,1,-1 do
		local	cur	= g[i]
		if( isGroup(cur) ) then
			rst,checkgrp	= _getObjByPos(cur,x,y,func)
			if( rst ) then
				-- 说明自对象里面找到合适的了
				return	rst
			end
			if( checkgrp ) then
				-- 看看group自身是否符合要求
				rst	= func(cur)
				if( rst ) then
					-- 说明group本身有touch方法且处理了这个消息
					return	rst
				end
			end
		else
			-- 如果不是group，则说明是显示对象
			-- 看看是否点中
			if( isPointInObj(cur,x,y) ) then
				-- 看看func判断是否可用
				local	rst	= func(cur)
				if( rst ) then
					-- func返回真表示就不用继续查找了
					return	rst
				end
				checkgrp	= true
			end
		end
	end
	-- 木有找到合适的
	return	nil, checkgrp
end
-- 判断点在屏幕中的哪个对象内部
function getObjByPos(x,y,func)
	return	_getObjByPos(display.getCurrentStage(), x, y, func)
end
